package com.imb.common.web.exception;

/**
 * @author xiaozhi 2023/7/7
 */

import com.imb.common.web.result.JsonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.authentication.*;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import javax.security.auth.login.AccountExpiredException;
import javax.security.sasl.AuthenticationException;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.util.List;

/**
 * 全局异常处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * 基础异常
     * @param e
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public JsonResult baseException(CustomException e){
        return JsonResult.error(e.getMessage());
    }

    /**
     * io异常
     * @param e
     * @return
     */
    @ExceptionHandler(IOException.class)
    public JsonResult IOException(IOException e){
        log.error(e.getMessage(),e);
        return JsonResult.error("请求异常，请稍后再试!");
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public JsonResult HTTPException(HttpMessageNotReadableException e){
        if(!e.getMessage().contains("Required request body is missing")){
            log.error(e.getMessage(), e);
        }
        return JsonResult.error("请求参数缺失，格式按照application/json");
    }

//    @ExceptionHandler(MissingServletRequestParameterException.class)
//    public JsonResult MissingServletRequestParameterException(MissingServletRequestParameterException e){
//        if(!e.getMessage().contains("Required request parameter")){
//            log.error(e.getMessage(), e);
//        }
//        return JsonResult.error("请求参数缺失，格式按照application/x-www-form-urlencoded");
//    }

//    @ExceptionHandler(NoHandlerFoundException.class)
//    public JsonResult handlerNoFoundException(Exception e)
//    {
//        log.error(e.getMessage(), e);
//        return JsonResult.error(HttpStatus.HTTP_NOT_FOUND, "路径不存在，请检查路径是否正确");
//    }

    /**
     * sql验证异常 唯一键冲突
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public JsonResult validatedSqlKeyException(DuplicateKeyException e)
    {
        log.error(e.getMessage(), e);
        return JsonResult.error(e.getCause().getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    public JsonResult handleRuntimeException(RuntimeException e) {
        log.error(e.getMessage(), e);
        return JsonResult.error(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public JsonResult handleException(Exception e) {
        log.error(e.getMessage(), e);
        return JsonResult.error("服务器跑路了，请稍后再试！");
    }

    @ExceptionHandler(BindException.class)
    public JsonResult validatedBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        // 参数类型不匹配检验
        StringBuilder msg = new StringBuilder();
        List<FieldError> fieldErrors = e.getFieldErrors();
        fieldErrors.forEach((oe) ->
                msg.append("参数:[").append(oe.getObjectName())
                        .append(".").append(oe.getField())
                        .append("]的传入值:[").append(oe.getRejectedValue()).append("]与预期的字段类型不匹配.")
        );
        log.error(String.valueOf(msg));
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return JsonResult.error(message);
    }




    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public JsonResult validConstraintViolationException(ConstraintViolationException e){
        log.error(e.getMessage(),e);
        String message=e.getConstraintViolations().iterator().next().getMessage();
        return JsonResult.error(message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validExceptionHandler(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage());
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return JsonResult.error(1000,message);
    }


    /**
     * spring security 异常
     *
     * -> AuthenticationException（认证异常）
     * -> AuthenticationException:认证异常的父类，抽象类
     * BadCredentialsException:登录凭证（密码）异常
     * InsufficientAuthenticationException:登陆凭证不够充分而抛出的异常
     * SessionAuthenticationException:会话并发管理时抛出的异常，例如会话总数超出最大限制数
     * UsernameNotFoundException:用户名不存在异常
     * PreAuthenticatedCredentialsNotFoundException:身份预认证失败异常
     * ProviderNotFoundException:未配置AuthenticationProvider异常
     * AuthenticationServiceException:由于系统问题而无法处理认证请求异常
     * InternalAuthenticationServiceException:由于系统问题而无法处理认证请求异常，和AuthenticationServiceException不同之处在于如果外部系统出错，不会抛出该异常
     * AuthenticationCredentialsNotFoundException:SecuityContext 中不存在认证主体时抛出的异常
     * NonceExpiredException:HTTP摘要认证时随机数过期异常
     * RememberMeAuthenticationException:RememberMe认证异常
     * CookieTheftException :RememberMe认证时Cookie被盗窃异常
     * InvalidCookieException:RememberMe认证时无效的Cookie异常
     * AccountStatusException:账户状态异常
     * LockedException:账户被锁定异常
     * DisabledException:账户被禁用异常
     * CredentialsExpiredException:登录凭证（密码）过期异常
     * AccountExpiredException:账户过期异常
     *
     * -> AccessDeniedException(权限异常)
     * AccessDeniedException ：权限异常的父类
     * AuthorizationServiceException: 由于系统问题而无法处理权限时抛出异常
     * CsrfException:Csrf令牌异常
     * MissingCsrfTokenException:Csrf令牌缺失异常
     * InvalidCsrfTokenException:Csrf令牌无效异常
     */

    @ExceptionHandler(UsernameNotFoundException.class)
    public JsonResult<Object> validUsernameNotFoundException(UsernameNotFoundException e)
    {
        log.error(e.getMessage()+":用户名不正确或密码不正确！");
        return JsonResult.error("用户名不正确或密码不正确！");
    }

    @ExceptionHandler(InternalAuthenticationServiceException.class)
    public JsonResult<Object> validInternalAuthenticationServiceException(InternalAuthenticationServiceException e)
    {
        log.error(e.getMessage()+":用户名不正确或密码不正确！");
        return JsonResult.error("用户名不正确或密码不正确！");
    }

    @ExceptionHandler(LockedException.class)
    public JsonResult<Object> validLockedException(LockedException e)
    {
        log.error(e.getMessage()+":账户被锁定，请联系管理员!");
        return JsonResult.error("账户被锁定，请联系管理员!");
    }

    @ExceptionHandler(CredentialsExpiredException.class)
    public JsonResult<Object> validCredentialsExpiredException(CredentialsExpiredException e)
    {
        log.error(e.getMessage()+":密码过期，请联系管理员!");
        return JsonResult.error("密码过期，请联系管理员!");
    }


    @ExceptionHandler(AccountExpiredException.class)
    public JsonResult<Object> validAccountExpiredException(AccountExpiredException e)
    {
        log.error(e.getMessage()+":账户过期，请联系管理员!");
        return JsonResult.error("账户过期，请联系管理员!");
    }

    @ExceptionHandler(DisabledException.class)
    public JsonResult<Object> validDisabledException(DisabledException e)
    {
        log.error(e.getMessage()+":账户被禁用，请联系管理员!");
        return JsonResult.error("账户被禁用，请联系管理员!");
    }

    @ExceptionHandler(BadCredentialsException.class)
    public JsonResult<Object> validBadCredentialsException(BadCredentialsException e)
    {
        log.error(e.getMessage()+":用户名不正确或密码不正确!");
        return JsonResult.error("用户名不正确或密码不正确!");
    }


    /**
     * 认证异常
     * @param e
     * @return
     */
    @ExceptionHandler(AuthenticationException.class)
    public JsonResult<Object> validAuthenticationException(AuthenticationException e)
    {
        log.error(e.getMessage()+":用户认证失败！请联系管理员");
        return JsonResult.error("用户认证失败！请联系管理员");
    }


    /**
     * 授权异常
     * @param e
     * @return
     */
    @ExceptionHandler(AccessDeniedException.class)
    public JsonResult<Object> validAccessDeniedException(AccessDeniedException e)
    {
        log.error(e.getMessage());
        return JsonResult.error(401,"没有权限!"+e.getMessage());
    }

}
